AWS ECS 꿀팁 2개

📅 2021. 10. 26

1. 용량 부족 문제 해결하기

ECS 서비스를 업데이트 할 때 종종 아래 에러를 볼 수 있다.

CannotPullContainerError: failed to register layer: Error processing tar file(exit status 1)

이미지가 없어서 못가져와! 라는 에러인데, 확인해보니 EC2 컨테이너에 디스크 용량이 꽉찬 탓에 새 이미지를 못 올려서 발생하는 에러 였다. EC2 컨테이너 안에서 docker images 명령어를 실행해보니 예전 버전의 이미지들이 자리를 차지하고 있었다. 업데이트 하면 자동으로 지워주는거 아니였어..? 배신감이 들었지만 구글링을 열심히 해보니 왜 옛날 이미지들이 지워지지 않았는지 원인을 알게되었다.

원인

  • 기본적으로 aws-ecs 도커 에이전트는 3시간마다 안쓰는 이미지들을 지우는데, 릴리즈를 짧은 시간내에 많이하게되면 용량이 더더욱 빨리 차게된다.
  • 1시간 안에 3번의 배포를 한 결과 올드 이미지가 인스턴스 디스크 용량 30GB/9GB (이미지 하나의 용량 3GB x 3 = 9GB)를 차지하고 있었다.

ECS가 이미지를 정리하지 않은게 아니고 다만 예전 이미지가 지워지기까지 3시간의 주기를 갖고 있었던 것이다.

해결

  1. ssh로 해당 EC2 인스턴스에 들어가서 docker container prune 실행
  2. docker images 로 예전 버전의 image id를 확인
  3. docker rmi <image_id> 로 이미지를 직접 삭제

하면 필요없는 이미지를 지워서 용량을 줄일 수 있다. 물론 이건 임시 해결방안이고, 근본적인 해결 방안은 다음과 같다.

  • EC2 인스턴스의 디스크 용량 늘리기 (대부분 30GB가 기본일 것이다)
  • 이미지 정리 텀 줄이기 (/etc/ecs/ecs.config 에서 ECS_ENGINE_TASK_CLEANUP_WAIT_DURATION 를 1h 로 바꾸면 한시간마다 정리할 수 있게 바꿀수 있다. - 참조 문서)
  • 도커 이미지 용량 줄이기 (참조 문서)

2. 동적 포트 설정하기

ECS에 서비스를 만들 때 정적 포트와 동적 포트 중 선택해서 만들수가 있다. 정적 포트는 30000, 30001, ... 처럼 서비스마다 포트를 고정해서 쓰는 방식이고 동적 포트는 ECS에서 놀고 있는 포트 중 하나를 찾아서 자동으로 컨테이너와 매핑해주는 방식이다.

우리 팀은 정적 포트 방식으로 서비스를 만들고 노션에 서비스별 포트를 기록해두는 방식을 사용하고 있었다. 지금은 레거시 서비스를 제외 한 대부분의 서비스를 동적 포트로 이전했는데, 아래 에러가 동적 포트로 이사를 결정한 원인이었다.

The closest matching container-instance <instance_id> is already using a port required by your task.

"이미 사용중인 포트가 있어서 서비스를 띄울 수 없습니다"

포트를 고정해놨는데 똑같은 포트를 쓰고 있다고..? 🤔🤔

서비스를 교체할 때 마다 위 에러 때문에 지연이 되는게 너무 답답했다. 열심히 구글링을 해본 결과, 원인을 찾을 수 있었다.

원인

서비스를 업데이트 할 때 ECS는 기존 서비스에 연결된 세션들을 안전하게 종료하고, 서비스를 교체할 준비가 됐으면 새 서비스를 띄우기 시작한다. 에러는 여기서 발생하는데, 하나의 EC2 인스턴스에 같은 포트로 매핑된 새 이미지를 띄우려고 하니 이미 사용중인 포트라고 에러가 뜨는 것이다.

종종 "기다리는 게" 해결 방법이 되는 이유는 기존 서비스가 컨테이너에서 종료되면 매핑될 포트의 자리가 비게 되므로 에러가 사라지는 것이다.

문제는 EC2 인스턴스를 여러개 띄워놨다면 문제가 해결될 수 있지만, task를 여러개 띄워야 하는 경우 다시 이 에러에 봉착할 수 있다는 것이다.

해결

동적 포트를 적용하면 앞에서 서술한 문제들을 쉽게 해결할 수 있다.

  1. EC2 콘솔의 대상 그룹 메뉴에서 업데이트 하려는 서비스에 연결된 대상 그룹을 선택하고 작업(Actions) > 상태 검사 편집(Edit health check settings) 누르고 고급 상태 검사 설정(Advanced health check settings) 섹션에서 포트를 재정의(Override) -> 트래픽 포트(Traffic port) 로 바꾼다.

  2. 작업 정의를 새로 생성하고 호스트 포트: 0 으로 바꾼다.

3. 생성한 작업정의로 서비스를 업데이트한다.

이렇게 하면 같은 인스턴스에서도 동적 포트 매핑이 적용되서 도커 포트 범위(32768 - 61000) 사이에서 자동으로 비는 포트를 찾아서 컨테이너를 띄운다. 단 선행 조건이 있는데

  1. 인바운드 규칙에 32768 - 61000 포트 범위의 규칙이 적용된 보안 그룹을 로드 밸런서에서 사용해야됨
  2. 로드 밸런서 종류가 Application Load Balancer 인 경우에만 사용 가능

동적 포트의 장점

  1. 다른 포트와 충돌을 방지하기 위해 일일이 기록할 필요가 없음
  2. 한 컨테이너에서 같은 서비스를 여러개 띄울 수 있음

무조건 동적 포트로 하는게 좋은거 아냐?! 라고 생각할 수도 있지만 Application Load Balancer 사용시에만 가능하다는 한계가 있다. Application Load Balancer 가 생기기 전에는 Classic Load Balancer 를 사용했었는데, 이 때는 정적 포트만 사용했기 때문에 레거시 서비스들은 아쉽게도 동적 포트를 쓸 수 없었다.